/*******************************************************************/
/*    Copyright (c) 1999-2020 by Spatial Corp.                     */
/*    All rights reserved.                                         */
/*    Protected by U.S. Patents 5,257,205; 5,351,196; 6,369,815;   */
/*                              5,982,378; 6,462,738; 6,941,251    */
/*    Protected by European Patents 0503642; 69220263.3            */
/*    Protected by Hong Kong Patent 1008101A                       */
/*******************************************************************/
//----------------------------- IopAcisFastBRep -------------------------------

#include <iostream>
#include <chrono>
#include <cwctype>
#include <unordered_set>

#include "SPAIop.h"
#include "SPAIopAcis.h"
#include "SPAIopPolicy.h"
#include "SPAIopPolicyFactory.h"
#include "SPAIopAcisSystem.h"
#include "SPAIopAcisError.h"
#include "lists.hxx"
#include "kernapi.hxx" 
#include "savres.hxx"
#include "fileinfo.hxx"
#include "SPAIopAcisSampleUtilities.h"
#include "SPAIopSampleUtilities.h"
#include "SPAIopSampleResultUtils.h"
#include "SPAIopPrintUtilities.h"
#include "SPAIopMultiProcessBatchDefinition.h"
#include "SPAIopMultiProcessSpoolerEx.h"
#include "SPAIopArgParser.h"

#ifdef _WINDOWS_SOURCE
#include <windows.h>
#include "Psapi.h"
#endif // _WINDOWS_SOURCE

// License file
#include "spatial_license.h" 
#include "spa_unlock_result.hxx"

using namespace SPAIopSampleLogging;

typedef std::pair<unsigned int, std::wstring> UniqueReferenceConfiguration;
std::vector<UniqueReferenceConfiguration> alreadyImportedRefList;
static std::vector<ENTITY_LIST*> toBeReleased;
static int _g_numProcessors = -1;	// -1 says use them all

SPAIopPolicy benchmarkPolicy;
std::wstring targetFilePath;
bool outputFileIsOK = false;

int numPartsFound = 0;
bool savedSuccessfully = false;

#define IMPORT_OK 0
#define IMPORT_PS_FAIL 201
#define IMPORT_PART_FAIL 202

int RegularImportPS(SPAIopInputProductStructure& pInputPS);
int RegularImportPSReference(SPAIopInputPSReference& reference, const SPAIopWString& iConfigName);
bool UserAlreadyImported(SPAIopInputPSReference& iReference, const SPAIopWString& iConfigName);
int ImportBrep(SPAIopInputPSReference& reference, SPAIopWString iConfigName, const wchar_t* pRefNameStr, SPAIopPartImportResult& oPartImportResult);
int ImportPartInContainer(SPAIopInputPSReference& iReference, ENTITY_LIST& ioEntities, const SPAIopWString& iConfigName, SPAIopPartImportResult& oPartImportResult);
void UserDoSomethingWithPartEntities(ENTITY_LIST& partEntities, const std::wstring& name, const SPAIopWString& iConfigName, unsigned int uniqueId);
void UserSaveAcisEntities(ENTITY_LIST& partEntities, const std::wstring& name, const SPAIopWString& iConfigName, unsigned int uniqueId);
static void GenerateSaveAcisEntitiesNameWithUniqueId(const std::wstring& iName, const SPAIopWString& iConfigName, unsigned int iUniqueId, std::wstring& oSaveName);
static bool GetConfigNameFromPolicy(const SPAIopPolicy& policy, SPAIopWString& configName, SPAIopWString& format);
int ProcessSinglePart(unsigned int refID, const SPAIopPolicy& policy, SPAIopInputProductStructure& iInputPS, ENTITY_LIST* oPartEntities, SPAIopRep availableRep);
static std::wstring GetFileName(const std::wstring& filePath);
size_t GetNonMissingParts(SPAIopInputProductStructure& iInputPSStructure, SPAIopPSImportResult& iInputPSResult);
void DoSomethingWithError(SPAIopAcisError& error);
int ImportPart(SPAIopPartDocument& iDoc, const SPAIopPolicy& iPolicy, SPAIopPartImportResult& oPartImportResult, ENTITY_LIST& oEntities);
bool ImportProductStructure(const wchar_t*& ipSourceFilePath, const SPAIopPolicy& iPolicy, SPAIopInputProductStructure& oInputPS, SPAIopPSImportResult& oInputPSResult); 

void printHelp()
{
	std::wcout << "Usage : IopAcisFastBRep -i <input file>] [-o <output file>] [-log <full-log-file-path>]" << std::endl;
	std::wcout << "IopAcisFastBRep      :" << std::endl;
	std::wcout << "-i                   : Input file." << std::endl;
	std::wcout << "[-o]                 : Output file (optional)." << std::endl;
	std::wcout << "[-log]               : Log file (optional)." << std::endl;
	std::wcout << "[-stdProc 1]         : Use standard processing for comparison. (0 default)." << std::endl;
	std::wcout << "-?                   : This help." << std::endl;
}

// ------------------------regularPSImplementation----------------------------------------------------------------------------------------
// User navigates imported PS, and can instantiate nodes in his own PSManager, and can import each Part node in a CGMContainer

int RegularImportPS(SPAIopInputProductStructure& pInputPS)
{
	// roots
	int nbInstances = pInputPS.GetRootInstancesCount();
	int i;
	int result = SPAIopSampleResult_Import_Ok;
	for (i = 0; i < nbInstances; i++)
	{
		SPAIopInputPSInstance iRootInstance = pInputPS.GetRootInstance(i);
		SPAIopWString instanceConfigName;
		SPAIopPSInstanceMiscData miscData = iRootInstance.GetMiscData();
		instanceConfigName = miscData.GetConfigurationName();

		SPAIopInputPSReference iReference = iRootInstance.GetReference();
		result = RegularImportPSReference(iReference, instanceConfigName);
	}

	alreadyImportedRefList.clear();
	return result;
}

int RegularImportPSReference(SPAIopInputPSReference& reference, const SPAIopWString& iConfigName)
{
	int result = SPAIopSampleResult_Import_Ok;

	if (reference.IsPart())
	{
		if (!UserAlreadyImported(reference, iConfigName))
		{
			unsigned int uniqueID = reference.GetUniqueID();
			SPAIopWString storageNameStr = reference.GetStorageName();
			SPAIopWString refNameStr = reference.GetReferenceName();
			SPAIopWString refTypeStr = reference.GetDefinitionDocumentType();
			const wchar_t* pStorageNameStr = NULL;
			storageNameStr.ToWChar(pStorageNameStr);

			const wchar_t* pRefNameStr = NULL;
			refNameStr.ToWChar(pRefNameStr);

			SPAIopPartImportResult partImportResult;
			result = ImportBrep(reference, iConfigName, pRefNameStr, partImportResult);
		}
	}
	else
	{
		int nbInstances = reference.GetInstancesCount();
		int i;
		for (i = 0; i < nbInstances; i++)
		{
			SPAIopInputPSInstance iInstance = reference.GetInstance(i);
			SPAIopInputPSReference iReference = iInstance.GetReference();

			SPAIopWString instanceConfigName;
			SPAIopPSInstanceMiscData miscData = iInstance.GetMiscData();
			instanceConfigName = miscData.GetConfigurationName();

			int res = RegularImportPSReference(iReference, instanceConfigName);
			if (0 != res)
				result = res;
		}
	}

	return result;
}

bool UserAlreadyImported(SPAIopInputPSReference& iReference, const SPAIopWString& iConfigName)
{
	bool alreadyImported(false);

	unsigned int currentID = iReference.GetUniqueID();
	const wchar_t* pCurrentConfigArray = NULL;
	iConfigName.ToWChar(pCurrentConfigArray);
	std::wstring currentConfigName = pCurrentConfigArray;

	for (std::vector<UniqueReferenceConfiguration>::iterator iter = alreadyImportedRefList.begin(); iter != alreadyImportedRefList.end(); ++iter)
	{
		unsigned int refID = iter->first;
		std::wstring configName = iter->second;
		if (currentID == refID && 0 == currentConfigName.compare(configName))
		{
			alreadyImported = true;
			break;
		}
	}
	if (false == alreadyImported)
	{
		alreadyImportedRefList.push_back(std::make_pair(currentID, currentConfigName));
	}

	return alreadyImported;
}

int ImportBrep(SPAIopInputPSReference& reference, SPAIopWString iConfigName, const wchar_t* pRefNameStr, SPAIopPartImportResult& oPartImportResult)
{
	int result = SPAIopSampleResult_Import_Ok;
	ENTITY_LIST partEntities;
	result = ImportPartInContainer(reference, partEntities, iConfigName, oPartImportResult);

	SPAIopWString upRefName = reference.GetReferenceName();
	const wchar_t* upRefNameStr = NULL;
	upRefName.ToWChar(upRefNameStr);
	std::wstring refName = upRefNameStr;
	UserDoSomethingWithPartEntities(partEntities, refName, iConfigName, reference.GetUniqueID());

	return result;
}

// -----------------------------------------------------------------------------------------------------------------------------

int ImportPartInContainer(SPAIopInputPSReference& iReference, ENTITY_LIST& ioEntities, const SPAIopWString& iConfigName, SPAIopPartImportResult& oPartImportResult)
{
	// Create and set Importer.
	SPAIopAcisPartImporter importer;
	SPAIopPartDocument refDoc = iReference.GetDocument();
	unsigned int uniqueID = iReference.GetUniqueID();
	SPAIopWString storageNameStr = iReference.GetStorageName();
	SPAIopWString refNameStr = iReference.GetReferenceName();
	SPAIopWString refTypeStr = iReference.GetDefinitionDocumentType();

	const wchar_t* pStorageNameStr = NULL;
	storageNameStr.ToWChar(pStorageNameStr);

	const wchar_t* pRefNameStr = NULL;
	refNameStr.ToWChar(pRefNameStr);

	const wchar_t* pConfigNameStr = NULL;
	iConfigName.ToWChar(pConfigNameStr);

	const wchar_t* pRefTypeNameStr = NULL;
	refTypeStr.ToWChar(pRefTypeNameStr);
	int result = SPAIopSampleResult_Import_Ok;

	try
	{
		oPartImportResult = importer.ImportWithPolicy(benchmarkPolicy, refDoc, ioEntities);

		//Get reference name after import - required for ProhibitLoad
		refNameStr = iReference.GetReferenceName();
		pRefNameStr = NULL;
		refNameStr.ToWChar(pRefNameStr);

		setlocale(LC_NUMERIC, "C");
	}
	catch (SPAIopAcisError& error)
	{
		const wchar_t* pMsg = nullptr;
		SPAIopWString msg = error.GetRequest();
		msg.ToWChar(pMsg);
		std::wstring sMsg(pMsg);
		if (sMsg == L"InterOp Aborted")
		{
			throw;
		}

		// error thrown in case of fatal outcome
		PrintMessage(L"\n FAILURE : Part failed to import. UniqueID = %d, Storage Name = %ls, Reference Name = %ls\n", uniqueID, pStorageNameStr, pRefNameStr);
		result = SPAIopSampleResult_Import_Part_Fail;
	}

	return result;
}

void UserDoSomethingWithPartEntities(ENTITY_LIST& partEntities, const std::wstring& name, const SPAIopWString& iConfigName, unsigned int uniqueId)
{
	numPartsFound++;
	if (outputFileIsOK)
	{
		UserSaveAcisEntities(partEntities, name, iConfigName, uniqueId);
	}
}

static void GenerateSaveAcisEntitiesNameWithUniqueId(const std::wstring& iName, const SPAIopWString& iConfigName, unsigned int iUniqueId, std::wstring& oSaveName)
{
	std::wstring resolvedName(L"refname");
	if (!iName.empty())
		resolvedName = iName;

	std::wstring idWString(L"refUID");
	if (iUniqueId >= 0)
	{
		std::wostringstream idString;
		idString << iUniqueId;
		idWString = idString.str();
	}

	std::wstring dotSeparator(L".");
	std::wstring sabExt(L"sab");

	const wchar_t* pConfigNameArray = NULL;
	iConfigName.ToWChar(pConfigNameArray);
	std::wstring configName = pConfigNameArray;

	std::unordered_set<wchar_t> charsToReplace = { L'*', L'/', L'\\', L'"', L'<', L'>',L':' };
	auto replaceChar = [&charsToReplace](wchar_t c) { if ((charsToReplace.find(c) != charsToReplace.end()) || std::iswspace(c)) return L'_'; else return c; };

	std::transform(configName.begin(), configName.end(), configName.begin(), replaceChar);
	std::transform(resolvedName.begin(), resolvedName.end(), resolvedName.begin(), replaceChar);

	if (configName.empty())
		configName = L"defaultConfig";

	oSaveName = targetFilePath + dotSeparator + resolvedName + dotSeparator + idWString.c_str() + dotSeparator + configName + dotSeparator + sabExt;

	// Replace ".." with "."
	std::wstring replacementPattern = L"..";
	size_t patternPos = 0;
	while ((patternPos = oSaveName.find(replacementPattern)) != std::wstring::npos)
	{
		oSaveName.replace(patternPos, replacementPattern.length(), L".");
	}
}

void UserSaveAcisEntities(ENTITY_LIST& partEntities, const std::wstring& name, const SPAIopWString& iConfigName, unsigned int uniqueId)
{
	if (partEntities.count() <= 0)
		return;

	bool isSaved = false;

	std::wstring saveName;

	//generate save name with uniqueid so there is no overwriting of files with the next part
	GenerateSaveAcisEntitiesNameWithUniqueId(name, iConfigName, uniqueId, saveName);

#ifdef _WINDOWS_SOURCE
	FILE* pFile = _wfopen(saveName.c_str(), L"wb");
	if (NULL != pFile)
	{
		FileInfo	info;
		info = *save_info;

		if (NULL == info.product_id())
			info.set_product_id("SPA-INTEROPERABILITY");

		if (0 > info.units())
			info.set_units(1.0);

		outcome out = api_set_file_info((FileIdent | FileUnits), info);
		out = api_save_entity_list(pFile, false, partEntities);
		fclose(pFile); pFile = NULL;
		isSaved = 1 == out.ok();
	}
#endif

	if (isSaved) savedSuccessfully = true;

	// When **completely** done with these entities, release them.
	// This is especially useful when sequentially importing and processing many parts in one session.
	SPAIopAcisSystem::ReleasePartEntities(partEntities);
	partEntities.clear();
}

//-----------------------------------------------------------------------------------------

bool ImportProductStructure(const wchar_t*& ipSourceFilePath, const SPAIopPolicy& iPolicy, SPAIopInputProductStructure& oInputPS, SPAIopPSImportResult& oInputPSResult)
{
	// Import the Assembly tree as a Product Structure.  Note that 
	// importing a Product Structure does not import any brep data associated
	// with the model, which is done separately from part-associated reference nodes
	// in the Product Structure.  Use try/catch exception handling blocks to 
	// manage fatal errors.

	bool returnValue = true;

	// Create Source Document from input file
	std::wstring sourceFilePath(ipSourceFilePath);
	SPAIopWString wSourceFilePath(sourceFilePath.c_str());
	SPAIopDocument sourceDoc(wSourceFilePath);

	// Create a Product Strcuture Importer
	SPAIopProductStructureImporter psImporter;

	try
	{
		oInputPSResult = psImporter.ImportWithPolicy(iPolicy, sourceDoc, oInputPS);
		SPAIopLengthUnitType inputPSUnit = oInputPS.GetUnits();
		if (SPAIopLengthUnit_Unknown != inputPSUnit)
		{
			SPAIopWString unitStr = SPAIopLengthUnitUtils::LengthUnitToString(inputPSUnit);
			const wchar_t* pUnitArray = NULL;
			unitStr.ToWChar(pUnitArray);
			std::wcout << "Product Structure Units: " << pUnitArray << std::endl;
		}

		DumpMissingFilesInProductStructure(oInputPSResult);
	}
	catch (SPAIopAcisError& error)
	{
		SPAIopAcisPrintUtils::DisplayError(error);
		returnValue = false;
	}
	catch (...)
	{
		std::wcout << "FAILURE: Unknown exception caught! " << std::endl;
		returnValue = false;
	}

	return returnValue;
}
//-----------------------------------------------------------------------------------------

size_t GetNonMissingParts(SPAIopInputProductStructure& iInputPSStructure, SPAIopPSImportResult& iInputPSResult)
{
	size_t missingParts = 0;
	size_t totalParts = iInputPSStructure.GetUniqueReferencesCount();
	SPAIopInputPSMissingFileIter missingFiles = iInputPSResult.GetMissingFileIterator();
	int nInstances = iInputPSStructure.GetUniqueReferencesCount();
	if (nInstances > 0)
	{
		int i = 0;
		for (i = 0; i < nInstances; i++)
		{

			SPAIopInputPSReference uniqueReference = iInputPSStructure.GetUniqueReference(i);
			SPAIopWString referenceStorageName = uniqueReference.GetStorageName();
			bool isMissingReference = missingFiles.ContainsPath(referenceStorageName);
			if (true == isMissingReference)
			{
				if (uniqueReference.IsPart())
				{
					missingParts++;
				}

			}
		}

	}
	return (totalParts - missingParts);

}

//get the configuration name, if any, from the policy
static bool GetConfigNameFromPolicy(const SPAIopPolicy& iPolicy, SPAIopWString& format, SPAIopWString& oConfigName)
{
	const wchar_t* wFormat = NULL;
	format.ToWChar(wFormat);

	bool retval = true;

	//pull correct options
	if (0 == wcscmp(L"SOLIDWORKS", wFormat))
	{
		SPAIopImportOptionsSolidWorks options;
		if (iPolicy.GetCurrentFormatImportOptions(options))
		{
			options.GetCurrentImportConfiguration(oConfigName);
		}
	}
	else if (0 == wcscmp(L"SWDirect", wFormat))
	{
		SPAIopImportOptionsSolidWorksDirect options;
		if (iPolicy.GetCurrentFormatImportOptions(options))
		{
			options.GetCurrentImportConfiguration(oConfigName);
		}
	}
	else if (0 == wcscmp(L"UG", wFormat))
	{
		SPAIopImportOptionsNX options;
		if (iPolicy.GetCurrentFormatImportOptions(options))
		{
			options.GetCurrentImportConfiguration(oConfigName);
		}
	}
	else if (0 == wcscmp(L"UGDirect", wFormat))
	{
		SPAIopImportOptionsNXDirect options;
		if (iPolicy.GetCurrentFormatImportOptions(options))
		{
			options.GetCurrentImportConfiguration(oConfigName);
		}
	}
	else if (0 == wcscmp(L"PROE", wFormat))
	{
		SPAIopImportOptionsCreo options;
		if (iPolicy.GetCurrentFormatImportOptions(options))
		{
			options.GetCurrentImportConfiguration(oConfigName);
		}
	}
	else
	{
		retval = false;
	}

	return retval;
}

//utility to return only file name with extension
static std::wstring GetFileName(const std::wstring& filePath)
{
	std::wstring fileName = filePath;
	std::size_t pos = fileName.find_last_of(L"/\\");
	if (std::wstring::npos != pos && pos < fileName.length())
	{
		fileName = fileName.substr(pos + 1);
	}

	return fileName;
}

void DoSomethingWithError(SPAIopAcisError& error)
{
	std::wcout << L"User API caught error errorID: " << error.GetErrorId() << std::endl;
	SPAIopWString req = error.GetRequest();
	SPAIopWString diagnostic = error.GetDiagnostic();
	SPAIopWString advice = error.GetAdvice();
	const wchar_t* pReqArray = NULL;
	req.ToWChar(pReqArray);
	std::wcout << "Error Message = " << pReqArray << std::endl;
	const wchar_t* pDiagnosticArray = NULL;
	diagnostic.ToWChar(pDiagnosticArray);
	std::wcout << "Error Diagnostic = " << pDiagnosticArray << std::endl;
	const wchar_t* pAdviceArray = NULL;
	advice.ToWChar(pAdviceArray);
	std::wcout << "Error Advice = " << pAdviceArray << std::endl;
}

int ImportPart(SPAIopPartDocument& iDoc, const SPAIopPolicy& iPolicy, SPAIopPartImportResult& oPartImportResult, ENTITY_LIST& oEntities)
{
	int result = SPAIopSampleResult_Import_Ok;

	SPAIopAcisPartImporter importer;
	try
	{
		oPartImportResult = importer.ImportWithPolicy(iPolicy, iDoc, oEntities);
	}
	catch (SPAIopAcisError& error)
	{
		result = SPAIopSampleResult_Import_Part_Fail;
		DoSomethingWithError(error);
		PrintMessage(L"\n FAILURE : Part failed to import mechanical.");
	}

	return result;
}

int ProcessSinglePart(unsigned int refID, const SPAIopPolicy& policy, SPAIopInputProductStructure& iInputPS, ENTITY_LIST* oPartEntities, SPAIopRep availableRep)
{
	int result = SPAIopSampleResult_Import_Ok;

	//obtain info from the input product structure for this part
	// reference
	SPAIopInputPSReference reference = iInputPS.GetReferenceFromId(refID);
	// part document
	SPAIopPartDocument doc = reference.GetDocument();
	// unique id
	unsigned int uniqueID = reference.GetUniqueID();
	// reference name
	SPAIopWString refName = reference.GetReferenceName();
	// reference type
	SPAIopWString refType = reference.GetDefinitionDocumentType();
	// storage name
	SPAIopWString storageName = reference.GetStorageName();

	//configuration name
	SPAIopWString configName;
	bool gotConfigName = GetConfigNameFromPolicy(policy, refType, configName);

	//Information for stdout
	const wchar_t* wName = NULL;
	refName.ToWChar(wName);
	std::wstring name = wName;
	const wchar_t* wType = NULL;
	refType.ToWChar(wType);
	std::wstring type = wType;
	const wchar_t* wStorage = NULL;
	storageName.ToWChar(wStorage);
	std::wstring storage = wStorage;
	std::wstring storageFileName = GetFileName(wStorage);
	std::wstring config = L"";
	if (gotConfigName)
	{
		const wchar_t* wConfig = NULL;
		configName.ToWChar(wConfig);
		config = wConfig;
	}

	numPartsFound++;

	//import the translated part
	if (availableRep == SPAIopRep_Mechanical )
	{
		//in this sample, we'll read in the acis entities and verify that they exist
		SPAIopPartImportResult importResult;
		int res = ImportPart(doc, policy, importResult, *oPartEntities);
		int entityCount = 0;
		if (NULL != oPartEntities)
		{
			entityCount = oPartEntities->count();
		}

		if (0 != res)
		{
			result = SPAIopSampleResult_Import_Part_Fail;
		}
	}

	return result;
}

//========================================================================================================

int test_IopAcisFastBRep(int argc, char** argv)
{
	auto startTime = std::chrono::steady_clock::now();

	int result = 0;

	// Work-flow similar to Acis based app where Acis will be generally initialized before IOP is called.
	api_start_modeller(0);

	// IOP is called in its own scope.
	// This sample uses a guard class that initializes InterOp upon 
	// construction and terminates InterOp upon destruction.  All 
	// translations are done within the boundaries of a single 
	// initialization and shutdown.  InterOp may only be initialized 
	// and shutdown once per process.  Please do not use the guard 
	// classes multiple times per process.
	SPAIopAcisSystemInitGuard initGuard(SPATIAL_LICENSE);

	result = initGuard.GetInitStatus();

	if (SPA_UNLOCK_NO_MSG == result)
	{
		// Spatial products have been unlocked
		std::wcout << L"SUCCESS: SPAIopAcisSystem::Initialize. " << std::endl;
	}
	else if (SPA_UNLOCK_KEY_WARN_EXPIRE == result)
		std::wcout << L"SPAIopAcisSystem::Initialize returned SPA_UNLOCK_KEY_WARN_EXPIRE; your license key will expire soon!\n";
	else if (SPA_UNLOCK_REGISTRATION_WARN_EXPIRE == result)
		std::wcout << L"SPAIopAcisSystem::Initialize returned SPA_UNLOCK_REGISTRATION_WARN_EXPIRE; your registration will expire soon.\n";
	else
	{
		std::wcout << L"SPAIopAcisSystem::Initialize returned an unexpected value; Contact Spatial Customer Support or your Account Manager.\n";
		return 1;
	}

	char* argFields[] = {
						  "i",
						  "o",
						  "stdProc",
						  "log"
	};

	int size = sizeof(argFields) / sizeof(*argFields);
	SPAIopArgParser parser(size, argFields, argc, argv, &printHelp);

	if (parser.IsSuccess())
	{
		std::wstring sourceFilePath;
		const wchar_t* pSourceFilePath = NULL;
		bool inputFileIsOK = parser.GetWStringValueFor("i", pSourceFilePath);

		if (inputFileIsOK && NULL != pSourceFilePath)
		{
			sourceFilePath = std::wstring(pSourceFilePath);
		}

		const wchar_t* pTargetFilePath = NULL;
		outputFileIsOK = parser.GetWStringValueFor("o", pTargetFilePath);
		if (outputFileIsOK && NULL != pTargetFilePath)
		{
			targetFilePath = std::wstring(pTargetFilePath);
		}

		const wchar_t* pLogFilePath = NULL;
		bool hasLogFileName = parser.GetWStringValueFor("log", pLogFilePath);

		int standardProc = 0;
		parser.GetIntegerValueFor("stdProc", standardProc);

		SPAIopInputProductStructure inputPS;
		SPAIopPSImportResult inputPSResult;

		if (!inputFileIsOK)
		{
			printHelp();
			if (sourceFilePath.empty())
			{
				std::wcout << L"FAILURE: Missing command argument - source file path is required" << std::endl;
				result = 200;
			}
		}
		else
		{
			if (standardProc == 1)
			{
				std::wcout << L"Standard processing..." << std::endl;
				benchmarkPolicy = SPAIopPolicyFactory::BuildPolicyDataProcessingMode(SPAIopPolicyDataProcessingMode_Standard);
			}
			else
				benchmarkPolicy = SPAIopPolicyFactory::BuildPolicyForBenchmark(SPAIopPolicyBenchmark_BRep);

			SetOrUpdateLogFileInPolicy(pLogFilePath, benchmarkPolicy);

			SPAIopWString wFilePath(sourceFilePath.c_str());
			SPAIopDocument iDoc(wFilePath);
			SPAIopProductStructureImporter psImporter;

			std::wcout << L"Importing PS..." << std::endl;
			try
			{
				inputPSResult = psImporter.ImportWithPolicy(benchmarkPolicy, iDoc, inputPS);// only imports PS (not the included parts)
				std::wcout << L"Traversing PS..." << std::endl;
				result = RegularImportPS(inputPS);
			}
			catch (SPAIopAcisError& error)
			{
				SPAIopAcisPrintUtils::DisplayError(error);
				result = -1;
			}
			catch (...)
			{
				std::wcout << "FAILURE: Unknown exception caught! " << std::endl;
				result = -1;
			}

			// When **completely** done with these entities, release them.
			// This is especially useful when sequentially importing and processing many parts in one session.
			//for (auto&& elist : toBeReleased) // nice, if your compiler supports it
			for (std::vector<ENTITY_LIST*>::iterator iter = toBeReleased.begin(); iter != toBeReleased.end(); ++iter)
			{
				SPAIopAcisSystem::ReleasePartEntities(**iter);
				ACIS_DELETE* iter;
			}
			toBeReleased.clear();

			std::wcout << L"Done!" << std::endl;
		}

		if (savedSuccessfully)
			std::wcout << L"SUCCESS: numParts " << numPartsFound << std::endl;
		else
			std::wcout << L"FAILURE: not able to save all parts" << std::endl;

		auto endTime = std::chrono::steady_clock::now();
		std::chrono::duration<double> diff = endTime - startTime;

		std::wcout << L"Time taken by process: " << diff.count() << " seconds\n";

		std::wcout << L"\n===========================================================================================\n\n";
	}

	return result;
}
